package com.sym.spring.servlet;



import com.sym.spring.annotation.Autowired;
import com.sym.spring.annotation.ResController;
import com.sym.spring.annotation.ResquestMapping;
import com.sym.spring.annotation.Service;


import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.*;


/**
 * 作    者： yingming shen
 * 修改时间： 2020/6/1 23:57.
 * 描   述：
 */
public class DispatchServlet  extends HttpServlet {

    private Properties configContext = new Properties();

    private Set<String> classNames = new HashSet<>();

    private Map<String,Object> ioc = new HashMap<>();

    private Map<String,Method> mappings = new HashMap<>();



    /**
     * 初始化
     */
    @Override
    public void init(ServletConfig servletConfig) throws ServletException {

        //加载配置
        doConfig(servletConfig);

        //扫描类名到类名容器
        doScanner(configContext.getProperty("scanPackage"));

        //初始化容器中的类
        doInstance();

        //依赖注入
        doDepencyInject();

        //扫描地址与方法的映射关系
        doMapping();

        //初始化完成
        System.out.println("mini Spring init sucess!");
    }

    private void doMapping() {
        for(Map.Entry<String, Object> entry : ioc.entrySet()) {
            Class<?> clazz = entry.getValue().getClass();

            if(!clazz.isAnnotationPresent(ResController.class)) continue;

            String baseUrl = "";

            if(clazz.isAnnotationPresent(ResquestMapping.class)) {
                ResquestMapping requestMapping = clazz.getAnnotation(ResquestMapping.class);
                baseUrl = requestMapping.value();
            }

            Method[] methods = clazz.getMethods();

            for(Method method : methods) {

                if(!method.isAnnotationPresent(ResquestMapping.class)) continue;

                ResquestMapping requestMapping = method.getAnnotation(ResquestMapping.class);

                String regex = requestMapping.value();

                regex = (baseUrl + regex).replaceAll("/+", "/");

                mappings.put(regex,method);

                System.out.println("Mapping: " + regex + "," + method.getName());
            }
        }
    }

    /**
     * 把加了autoWired的字段赋值
     */
    private void doDepencyInject() {
        for (Map.Entry<String, Object> entry : ioc.entrySet()) {
            Class<?> clazz = entry.getValue().getClass();

            for (Field field : clazz.getDeclaredFields()) {
                if (field.isAnnotationPresent(Autowired.class)) {
                    Autowired annotation = field.getAnnotation(Autowired.class);
                    String beanName = annotation.value();
                    if ("".equals(beanName)) {
                        beanName = lowerFirstChar(field.getType().getSimpleName());
                    }
                    field.setAccessible(true);
                    try {
                        field.set(entry.getValue(),ioc.get(beanName));
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }

                }
            }
        }

    }

    private void doInstance() {
        for (String className : classNames) {
            try {
                Class clazz = Class.forName(className);
                String beanName = lowerFirstChar(clazz.getSimpleName()) ;
                if (clazz.isAnnotationPresent(ResController.class)) {
                    Object instance = clazz.newInstance();

                    ioc.put(beanName,instance);
                    System.out.println(className+" 实例化成功");
                }else if(clazz.isAnnotationPresent(Service.class)){
                    Service  annotation = (Service) clazz.getAnnotation(Service.class);
                    String value = annotation.value();
                    if (!"".equals(value)) {
                        ioc.put(value,clazz.newInstance());
                    }else {
                        ioc.put(beanName,clazz.newInstance());
                    }

                    //接口吗，吗，名赋值实体
                    Class[] interfaces = clazz.getInterfaces();
                    for (Class interFace : interfaces) {
                        ioc.put( lowerFirstChar(interFace.getSimpleName()),clazz.newInstance());
                    }
                    System.out.println(className+" 实例化成功");
                }else {
                    System.out.println(className + " 为实例化");
                }



            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InstantiationException e) {
                e.printStackTrace();
            }
        }
    }

    private String lowerFirstChar(String className) {
        char[] chars = className.toCharArray();
        chars[0] += 32;
        return String.valueOf(chars);
    }

    private void doScanner(String  scanPackage) {
        String replace = scanPackage.replaceAll("\\.", "/");
        URL url = this.getClass().getClassLoader().getResource("/" + replace);
        File dir = new File(url.getFile());
        for (File file : dir.listFiles()) {
            if (file.isDirectory()) {
                doScanner(scanPackage+"."+file.getName());
            }else{
                String className = scanPackage + "." + file.getName().replace(".class", "");
                classNames.add(className);
                System.out.println(className);
            }
        }

    }

    private void doConfig(ServletConfig config) {
        String configLocation = config.getInitParameter("contextConfigLocation");
        InputStream is = this.getClass().getClassLoader().getResourceAsStream(configLocation);
        try {
            configContext.load(is);
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if (null != is) {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }


    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String method = req.getMethod();
        if ("POST".equals(method)||"GET".equals(method)) {
            doPost(req,resp);
        }else {
            System.out.println("不支持的访问类型！");
        }
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String url = req.getRequestURI();
        String contextPath = req.getContextPath();
        url = url.replaceAll(contextPath,"").replaceAll("/+","/");
        if (!mappings.containsKey(url)) {
            resp.getWriter().write("404 没有找到接口");
            return;
        }

        Map<String,String[]> parms = req.getParameterMap();

        try {
            Method method = mappings.get(url);
            String beanName = lowerFirstChar(method.getDeclaringClass().getSimpleName());

            method.invoke(ioc.get(beanName), new Object[]{req,resp});
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

}
